package com.test.ayi.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 支持复杂 SpEL 表达式的自定义注解
 * 可以在方法和字段上使用
 * 支持通过 beanName 调用方法获取返回值
 */
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpelAnnotation {
    String value();
}

@Component
class SpelAnnotationProcessor implements BeanPostProcessor, ApplicationContextAware {
    
    private ApplicationContext applicationContext;
    private Environment environment;
    private final ExpressionParser parser = new SpelExpressionParser();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        this.environment = applicationContext.getEnvironment();
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        processFields(bean);
        processMethods(bean);
        return bean;
    }

    private void processFields(Object bean) {
        ReflectionUtils.doWithFields(bean.getClass(), field -> {
            SpelAnnotation annotation = AnnotationUtils.getAnnotation(field, SpelAnnotation.class);
            if (annotation != null) {
                Object value = evaluateSpel(annotation.value(), bean);
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field, bean, value);
            }
        });
    }

    private void processMethods(Object bean) {
        ReflectionUtils.doWithMethods(bean.getClass(), method -> {
            try {
                SpelAnnotation annotation = AnnotationUtils.getAnnotation(method, SpelAnnotation.class);
                if (annotation != null && method.getParameterCount() == 1) {
                    Object value = evaluateSpel(annotation.value(), bean);
                    ReflectionUtils.makeAccessible(method);
                    method.invoke(bean, value);
                }
            } catch (Exception e) {
                throw new IllegalStateException("Error processing @SpelAnnotation on method: " + method.getName(), e);
            }
        });
    }

    private Object evaluateSpel(String expression, Object bean) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(new BeanFactoryResolver(applicationContext));
        context.setRootObject(bean);
        
        // 添加 environment 变量到上下文
        context.setVariable("environment", environment);
        
        // 如果是纯属性占位符表达式（${...}），包装成SpEL表达式
        if (expression.startsWith("${")) {
            expression = "#{#environment.getProperty('" + 
                       expression.substring(2, expression.lastIndexOf("}")) + 
                       "'" +
                       (expression.contains(":") ? 
                           ",'" + expression.substring(expression.indexOf(":") + 1, expression.length() - 1) + "'" 
                           : "") +
                       ")}";
        }
        
        // 如果表达式包含在 #{...} 中，去掉这个包装
        if (expression.startsWith("#{") && expression.endsWith("}")) {
            expression = expression.substring(2, expression.length() - 1);
        }
        
        return parser.parseExpression(expression).getValue(context);
    }
}
